#include <opencv2/opencv.hpp>
#include <iostream>
#include <chrono>
#include <sys/time.h>
#include <unistd.h>

using namespace std;
using namespace cv;

#define MILLION 1000000L

int main(int, char *argv[])
{
    Mat in_image, out_image;
    //读取原始图像
    in_image = imread("../JapanXB.jpg", IMREAD_GRAYSCALE);
    if (in_image.empty()) {
        //检查是否读取图像
        cout << "Error! Input image cannot be read...\n";
        return -1;
    }else{
        cout << "Read image OK :) " << endl;
        cout << "rows: " << in_image.rows << endl;
        cout << "cols: " << in_image.cols << endl;
        cout << "channels/depthts: " << in_image.channels() << endl;
    }
    
    cout << "Traverse all pixels by " << endl;
    /**************************************************************/
    //元素大小字节数
    int es = in_image.elemSize();
    cout << "elemSize: " << es << endl;
    cout << "step: " << in_image.step[0] << endl;
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
    //使用地址遍历，地址遍历不一定连续的Mat
    for (int row = 0; row < in_image.rows; row++)
    {
        for (int col = 0; col < in_image.cols; col++)
        {
            (&in_image.data[row*in_image.step])[col*es] = 255;//B
            //(&in_image.data[row*in_image.step])[col*es+1] = 0;//G
            //(&in_image.data[row*in_image.step])[col*es+2] = 255;//R
        }
    }
	chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
	chrono::duration<double> time_used_A = chrono::duration_cast<chrono::duration<double>>(t2 - t1)*MILLION;
    cout << "Method A cost " << time_used_A.count() << "us" << endl;


    /**************************************************************/
    chrono::steady_clock::time_point t3 = chrono::steady_clock::now();
    //使用ptr遍历Mat
    for (int row = 0; row < in_image.rows; row++)
    {
        for (int col = 0; col < in_image.cols; col++)
        {
            // Vec3b *c = mat.ptr<Vec3b>(row, col);
            // c->val[0] = 0;        //B
            // c->val[1] = 255;    //G
            // c->val[2] = 0;        //R
            uchar *c = in_image.ptr<uchar>(row, col);
            c[0] = 0;
        }
    }
	chrono::steady_clock::time_point t4 = chrono::steady_clock::now();
	chrono::duration<double> time_used_B = chrono::duration_cast<chrono::duration<double>>(t4 - t3)*MILLION;
    cout << "Method B cost " << time_used_B.count() << "us" << endl;


    /**************************************************************/
    chrono::steady_clock::time_point t5 = chrono::steady_clock::now();
    //使用at来遍历
    for (int row = 0; row < in_image.rows; row++)
    {
        for (int col = 0; col < in_image.cols; col++)
        {
            // Vec3b &m = in_image.at<Vec3b>(row, col);
            // m[0] = 100;
            // m[1] = 100;
            // m[2] = 100;
            uchar &m = in_image.at<uchar>(row,col);
            m = 125;
        }
    }
	chrono::steady_clock::time_point t6 = chrono::steady_clock::now();
	chrono::duration<double> time_used_C = chrono::duration_cast<chrono::duration<double>>(t6 - t5)*MILLION;
    cout << "Method C cost " << time_used_C.count() << "us" << endl;


    /**************************************************************/
    chrono::steady_clock::time_point t7 = chrono::steady_clock::now();
    //通过迭代器来遍历
    // auto it = in_image.begin<Vec3b>();
    // auto it_end = in_image.end<Vec3b>();
    auto it = in_image.begin<uchar>();
    auto it_end = in_image.end<uchar>();
    for (; it != it_end; it++)
    {
        // (*it).val[0] = 0;    //B
        // (*it).val[1] = 0;    //G
        // (*it).val[2] = 255;    //R
        *it = 200;
    }
	chrono::steady_clock::time_point t8 = chrono::steady_clock::now();
	chrono::duration<double> time_used_D = chrono::duration_cast<chrono::duration<double>>(t8 - t7)*MILLION;
    cout << "Method D cost " << time_used_D.count() << "us" << endl;


    //创建窗口
    namedWindow("Origin", WINDOW_NORMAL);
    //在之前创建的窗口中显示图片
    imshow("Origin", in_image);
    cout << "Press any key to exit...\n";
    waitKey(); // Wait for key press
    return 0;
}

/*
对于灰度图像：0-黑色；255-白色
*/

/*
1s = 1000ms = 1000000us
*/

/*
enum { IMREAD_UNCHANGED = -1, // 8 位，彩色或非彩色
    IMREAD_GRAYSCALE = 0, // 8 位，灰度
    IMREAD_COLOR = 1, //未改变深度、颜色
    IMREAD_ANYDEPTH = 2, //任意深度，未改变颜色
    IMREAD_ANYCOLOR = 4, //未改变深度，任意颜色
    IMREAD_LOAD_GDAL = 8 // 使用 gdaL 驱动程序
};

enum { WINDOW_NORMAL = 0x00000000,
    //用户可以调整窗口的大小（无约束）
    //还可以将一个全屏窗口切换为常规窗口
    WINDOW_AUTOSIZE = 0x00000001,
    //用户不能调整窗口的大小，
    //窗口的大小由显示的图像所限制
    WINDOW_OPENGL = 0x00001000, // OpenGL 支持的窗口
    WINDOW_FULLSCREEN = 1,
    WINDOW_FREERATIO=0x00000100,
    //图像可任意使用（调整图像时可没有比例限制）
    WINDOW_KEE PRATIO = 0x00000000
    //调整图像时保持图像的缩放比例
};
*/

/*
参考：
https://www.cnblogs.com/wangguchangqing/p/4016179.html
Mat的作用

The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ).

上面的一段话引用自官方的文档，Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵，灰度或彩色图像，立体元素，点云，张量以及直方图（高维的直方图使用SparseMat保存比较好）。简而言之，Mat就是用来保存多维的矩阵的。
Mat的常见属性

    data  uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针，data就是指向矩阵数据的指针。
    dims 矩阵的维度，例如5*6矩阵是二维矩阵，则dims=2，三维矩阵dims=3.
    rows  矩阵的行数
    cols   矩阵的列数
    size 矩阵的大小，size(cols,rows),如果矩阵的维数大于2，则是size(-1,-1)
    channels 矩阵元素拥有的通道数，例如常见的彩色图像，每一个像素由RGB三部分组成，则channels = 3

下面的几个属性是和Mat中元素的数据类型相关的。
    type
    表示了矩阵中元素的类型以及矩阵的通道个数，它是一系列的预定义的常量，其命名规则为CV_(位数）+（数据类型）+（通道数）。具体的有以下值：
    CV_8UC1 	CV_8UC2 	CV_8UC3 	CV_8UC4
    CV_8SC1 	CV_8SC2 	CV_8SC3 	CV_8SC4
    CV_16UC1 	CV_16UC2 	CV_16UC3 	CV_16UC4
    CV_16SC1 	CV_16SC2 	CV_16SC3 	CV_16SC4
    CV_32SC1 	CV_32SC2 	CV_32SC3 	CV_32SC4
    CV_32FC1 	CV_32FC2 	CV_32FC3 	CV_32FC4
    CV_64FC1 	CV_64FC2 	CV_64FC3 	CV_64FC4
    这里U（unsigned integer）表示的是无符号整数，S（signed integer）是有符号整数，F（float）是浮点数。
    例如：CV_16UC2，表示的是元素类型是一个16位的无符号整数，通道为2.
    C1，C2，C3，C4则表示通道是1,2,3,4
    type一般是在创建Mat对象时设定，如果要取得Mat的元素类型，则无需使用type，使用下面的depth
    depth
    矩阵中元素的一个通道的数据类型，这个值和type是相关的。例如 type为 CV_16SC2，一个2通道的16位的有符号整数。那么，depth则是CV_16S。depth也是一系列的预定义值，
    将type的预定义值去掉通道信息就是depth值:
    CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
    elemSize
    矩阵一个元素占用的字节数，例如：type是CV_16SC3，那么elemSize = 3 * 16 / 8 = 6 bytes
    elemSize1
    矩阵元素一个通道占用的字节数，例如：type是CV_16CS3，那么elemSize1 = 16  / 8 = 2 bytes = elemSize / channels
    
    step
    Mat中的step是一个MStep的一个实例。其声明如下： 
    step[0]是矩阵中一行元素的字节数。
    step[1]是矩阵中一个元素的自己数，也就是和上面所说的elemSize相等。

    上面说到，Mat中一个uchar* data指向矩阵数据的首地址，而现在又知道了每一行和每一个元素的数据大小，就可以快速的访问Mat中的任意元素了。下面公式：

    addr(M_{i,j}) = M.data + M.step[0]*i + M.step[1]*j

    step1()
    规整化的step，值为step / elemSize1。
*/